<template>
  <div class="json-editor">
    <textarea ref="textarea"/>
  </div>
</template>

<script>
import CodeMirror from 'codemirror'
import 'codemirror/mode/javascript/javascript'
import 'codemirror-formatting'
import 'codemirror/addon/fold/foldcode'
import 'codemirror/addon/fold/foldgutter'
import 'codemirror/addon/fold/brace-fold'
import 'codemirror/addon/fold/comment-fold'
import 'codemirror/addon/lint/lint'
import 'codemirror/addon/lint/json-lint'
import 'codemirror/addon/selection/active-line'

import 'codemirror/addon/lint/lint.css'
import 'codemirror/lib/codemirror.css'
import 'codemirror/addon/fold/foldgutter.css'
import 'codemirror/theme/idea.css'
import 'codemirror/theme/eclipse.css'
import 'codemirror/theme/rubyblue.css'
import 'codemirror/theme/duotone-light.css'
import 'codemirror/theme/monokai.css'
import 'codemirror/theme/mdn-like.css'
import 'codemirror/theme/xq-light.css'
import 'codemirror/theme/dracula.css'
import 'codemirror/theme/material.css'
import 'codemirror/theme/material-darker.css'

// eslint-disable-next-line import/no-webpack-loader-syntax
require('script-loader!jsonlint')

export default {
  name: 'MrJsonEditor',
  props: {
    value: {
      type: String
    },
    showNumber: {
      type: Boolean,
      default: true
    },
    mode: {
      type: String,
      default: 'application/json'
    },
    theme: {
      type: String,
      default: 'idea'
    },
    lint: {
      type: Boolean,
      default: true
    },
    readonly: {
      type: Boolean,
      default: false
    },
    indentUnit: {
      type: Number,
      default: 4
    },
    smartIndent: {
      type: Boolean,
      default: true
    },
    lineWrap: {
      type: Boolean,
      default: false
    },
    gutter: {
      type: Boolean,
      default: true
    },
    autoFormat: { // 进入时是否自动格式化
      type: Boolean,
      default: true
    }
  },
  data () {
    return {
      jsonEditor: false
    }
  },
  watch: {
    value (value) {
      const editorValue = this.jsonEditor.getValue()
      if (value !== editorValue) {
        this.jsonEditor.setValue(value)
        // 触发校验
        this.dispatch('AFormItem', 'on-form-change', value)
      }
    },
    showNumber (val) {
      this.jsonEditor && this.jsonEditor.setOption('lineNumbers', val)
    },
    lint (val) {
      this.jsonEditor && this.jsonEditor.setOption('lint', val)
    },
    readonly (val) {
      this.jsonEditor && this.jsonEditor.setOption('readOnly', val)
    },
    theme (val) {
      this.jsonEditor && this.jsonEditor.setOption('theme', val)
    }
  },
  mounted () {
    this.jsonEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
      lineNumbers: this.showNumber,
      mode: this.mode,
      gutters: ['CodeMirror-lint-markers', 'CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
      styleActiveLine: true,
      theme: this.theme,
      lint: this.lint,
      readOnly: this.readonly,
      indentUnit: this.indentUnit, // 缩进单位，默认2
      smartIndent: this.smartIndent,
      lineWrapping: this.lineWrap, // 代码换行
      foldGutter: this.gutter,
      autoRefresh: true, // 自动触发刷新
      // 快捷键
      extraKeys: {
        F7: function autoFormat (cm) {
          const totalLines = cm.lineCount()
          cm.autoFormatRange({ line: 0, ch: 0 }, { line: totalLines })
        } // 代码格式化
      }
    })
    this.jsonEditor.setSize('100%', '100%')
    this.jsonEditor.setValue(this.value)
    if (this.autoFormat && this.value && this.value.length > 0) {
      try {
        const formatStr = JSON.stringify(JSON.parse(this.value), null, this.indentUnit)
        this.$emit('on-change', formatStr)
        this.$emit('input', formatStr)
      } catch (e) {
        this.formatCode()
      }
    }
    this.jsonEditor.on('change', cm => {
      this.$emit('on-change', cm.getValue())
      this.$emit('input', cm.getValue())
    })
    this.jsonEditor.on('blur', this.handleBlur)
    this.$emit('on-init', this.jsonEditor)
  },
  methods: {
    getEditor () {
      return this.jsonEditor
    },
    getValue () {
      return this.jsonEditor.getValue()
    },
    formatCode () {
      if (this.jsonEditor) {
        const totalLines = this.jsonEditor.lineCount()
        this.jsonEditor.autoFormatRange({ line: 0, ch: 0 }, { line: totalLines })
      }
    },
    handleBlur (event) {
      this.$emit('on-blur', event)
      // 触发校验
      this.dispatch('AFormItem', 'on-form-blur', this.value)
    },
    refresh () {
      this.jsonEditor && this.jsonEditor.refresh()
    },
    dispatch (componentName, eventName, params) {
      let parent = this.$parent || this.$root
      let name = parent.$options.name

      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent

        if (parent) {
          name = parent.$options.name
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params))
      }
    }
  }
}
</script>

<style lang="less" scoped>
.json-editor {
  line-height: 20px;
  min-height: 100px;
  height: 300px;
  min-width: 300px;
  width: 100%;
  border: 1px solid #D9D9D9;

  /deep/ .CodeMirror-linenumber {
    padding-right: 10px;
  }
  /deep/ .CodeMirror-gutters {
    border-right: 0;
  }

  /deep/ pre {
    /deep/ .CodeMirror-Line {
      background-color: #69b7ed !important;
    }
  }
}
</style>
